package com.py.framework.core.base.service;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.py.framework.core.annotation.AdditionalColumn;
import com.py.framework.core.annotation.ShowColumn;
import com.py.framework.core.base.mapper.BaseMapper;
import com.py.framework.core.common.OptionalColumn;
import com.py.framework.core.common.Page;
import com.py.framework.core.exception.ServiceException;
import com.py.framework.core.helper.HttpServletHelper;
import com.py.framework.core.log.ExceptionLogger;
import com.py.framework.core.utils.DateUtils;
import com.py.framework.core.utils.MapCache;
import com.py.framework.core.utils.SqlBuilder;
import com.py.framework.core.utils.WebUtil;

public class BaseServiceImpl<T, M extends BaseMapper<T>> implements BaseService<T> {

	@Autowired
	private M mapper;

	@Override
	public T select(String id) {

		return mapper.select(id);
	}

	@Override
	public void delete(String id) {
		mapper.delete(id);
	}

	@Override
	public void update(T record) {

		mapper.update(record);
	}

	@Override
	public void add(T record) {

		mapper.add(record);
	}

	@Override
	public List<T> getListByConditionForPage(Map<String, Object> params, String condition, Page page, String tableId) {

		List<String> conditions = SqlBuilder.createConditionSql(condition);

		List<String> defaultShowColumns = new ArrayList<String>();

		List<String> additinalColumn = new ArrayList<String>();
		try {
			buildDefaultShowAndAdditinalColumns(defaultShowColumns, additinalColumn, tableId, getEntityClass());
		} catch (Exception e) {
			ExceptionLogger.error(e);
		}
		return mapper.selectListByConditionForPage(params, conditions, defaultShowColumns,
				additinalColumn.toArray(new String[additinalColumn.size()]), page);

	}

	@Override
	public void write2Excel(Map<String, Object> params, String condition, String columnEN, Page page, String fileName,
			String sheetName) throws IOException {

		page.setLimit(1000);
		List<String> conditions = SqlBuilder.createConditionSql(condition);
		List<String> showColumns = JSONArray.parseArray(columnEN, String.class);
		List<T> data = mapper.selectListByConditionForPage(params, conditions, showColumns, null, page);
		write2Excel(data, showColumns, fileName, sheetName, getEntityClass());

	}

	@Override
	public List<T> getListForPage(Map<String, Object> params, Page page) {

		return mapper.selectListForPage(params, page);

	}

	@Override
	public List<OptionalColumn> getOptionalColumns() {

		return assembleOptionColumn(getEntityClass());
	}

	@Override
	public List<OptionalColumn> getAdvancedOptionalColumns() {

		return assembleOptionColumn(getEntityClass());
	}

	@Override
	public void addShowColumns(String[] columns, String suffix) {

		HttpServletRequest request = WebUtil.getRequest();

		String userToken = HttpServletHelper.getUserToken(request);

		if (StringUtils.isNotBlank(userToken)) {

			MapCache.set(userToken + "_" + suffix, Arrays.asList(columns));

		}

	}

	@SuppressWarnings("unchecked")
	private List<OptionalColumn> assembleOptionColumn(Class<?> clas) {

		List<OptionalColumn> columns = new ArrayList<>();

		Field[] declaredFields = clas.getDeclaredFields();

		for (Field field : declaredFields) {
			if (field.isAnnotationPresent(ShowColumn.class)) {
				String genericType = field.getGenericType().toString();
				ShowColumn annotation = field.getAnnotation(ShowColumn.class);
				String alias = annotation.alias();
				String tag = annotation.tag();
				int order = annotation.order();
				boolean fuzzy = annotation.fuzzy();

				if ("int".equals(genericType) || "double".equals(genericType) || "float".equals(genericType)
						|| "long".equals(genericType) || "class java.lang.Double".equals(genericType)
						|| "class java.lang.Long".equals(genericType) || "class java.lang.Float".equals(genericType)
						|| "class java.lang.Integer".equals(genericType)
						|| "class java.math.BigDecimal".equals(genericType)) {

					OptionalColumn optionalColumn = new OptionalColumn(alias, tag, "number", true, "input", false, null,
							false, order);

					columns.add(optionalColumn);

				} else if ("class java.lang.String".equals(genericType)) {

					OptionalColumn optionalColumn = null;

					String widgetValue = annotation.widgetValue();

					HashMap<String, String> widgetValueMap;

					if (StringUtils.isNotBlank(widgetValue)) {

						try {

							widgetValueMap = (HashMap<String, String>) JSON.parseObject(widgetValue, Map.class);

						} catch (Exception e) {

							ExceptionLogger.error(e);

							widgetValueMap = new HashMap<>();

						}

						optionalColumn = new OptionalColumn(alias, tag, "varchar", true, "select", false,
								widgetValueMap, false, order);

					} else {

						optionalColumn = new OptionalColumn(alias, tag, "varchar", true, "input", fuzzy, null, true,
								order);

					}

					columns.add(optionalColumn);

				} else if ("class java.util.Date".equals(genericType)
						|| "class java.sql.Timestamp".equals(genericType)) {

					OptionalColumn optionalColumn = new OptionalColumn(alias, tag, "timestamp", true, "input", false,
							null, false, order);

					columns.add(optionalColumn);
				}

			}
		}

		Collections.sort(columns, new Comparator<OptionalColumn>() {
			@Override
			public int compare(OptionalColumn o1, OptionalColumn o2) {
				Integer i1 = o1.getOrder();
				Integer i2 = o2.getOrder();
				return i1.compareTo(i2);
			}
		});

		return columns;

	}

	private Class<?> getEntityClass() {
		return (Class<?>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	@SuppressWarnings("unchecked")
	private void buildDefaultShowAndAdditinalColumns(List<String> defaultShowColumns, List<String> additinalColumn,
			String tableId, Class<?> clazz) {

		HttpServletRequest request = WebUtil.getRequest();

		String userToken = HttpServletHelper.getUserToken(request);

		List<String> showColumns = null;

		if (StringUtils.isNotBlank(userToken)) {

			showColumns = (List<String>) MapCache.get(userToken + "_" + tableId);

		}

		// 用户未自定义查询列加载默认显示列
		if (CollectionUtils.isEmpty(showColumns)) {

			Field[] declaredFields = clazz.getDeclaredFields();

			for (Field field : declaredFields) {

				if (field.isAnnotationPresent(ShowColumn.class)) {

					ShowColumn annotation = field.getAnnotation(ShowColumn.class);

					defaultShowColumns.add(annotation.alias());

				} else if (field.isAnnotationPresent(AdditionalColumn.class)) {

					AdditionalColumn annotation = field.getAnnotation(AdditionalColumn.class);

					additinalColumn.add(annotation.alias());

				}

			}

		} else {

			defaultShowColumns.addAll(showColumns);
		}

		if (CollectionUtils.isEmpty(defaultShowColumns)) {

			throw new ServiceException("至少选择一列");

		}
	}

	private void write2Excel(List<T> data, List<String> showColumns, String fileName, String sheetName, Class<?> clazz)
			throws IOException {
		List<OptionalColumn> allColumns = getOptionalColumns();
		List<OptionalColumn> columns = new ArrayList<>();
		HSSFWorkbook workbook = new HSSFWorkbook();
		HSSFSheet sheet = workbook.createSheet();
		workbook.setSheetName(0, sheetName);

		// 创建标题
		HSSFRow titleRow = sheet.createRow(0);
		for (int i = 0; i < showColumns.size(); i++) {
			HSSFCell titleCell = titleRow.createCell(i, HSSFCell.CELL_TYPE_STRING);
			for (OptionalColumn column : allColumns) {
				if (showColumns.get(i).equals(column.getColNameEN())) {
					titleCell.setCellValue(column.getColNameCN());
					columns.add(column);
					break;
				}
			}
		}

		for (int i = 0; i < data.size(); i++) {

			T t = data.get(i);
			// 数据行从1开始，标题占了第0行
			HSSFRow row = sheet.createRow(i + 1);
			for (int j = 0; j < columns.size(); j++) {
				OptionalColumn column = columns.get(j);
				String widget = column.getWidget();
				String type = column.getType();
				String colNameEN = column.getColNameEN();
				String colNameENCapital = Character.toUpperCase(colNameEN.charAt(0)) + colNameEN.substring(1);
				HashMap<String, String> widgetValue = column.getWidgetValue();

				try {
					Method method = clazz.getMethod("get" + colNameENCapital);
					Object invoke = method.invoke(t);
					HSSFCell cell = row.createCell(j, HSSFCell.CELL_TYPE_STRING);
					String columValue = "";
					if (invoke != null) {
						columValue = invoke.toString();
						if ("select".equals(widget) && widgetValue != null) {
							columValue = widgetValue.get(columValue) != null ? widgetValue.get(columValue) : "";
						} else if ("timestamp".equals(type)) {
							columValue = DateUtils.dateToString((Date) invoke);
						}
						cell.setCellValue(columValue);
					}

				} catch (Exception e) {
					ExceptionLogger.error(e);
				}
			}

		}

		HttpServletResponse response = WebUtil.getResponse();
		fileName = URLEncoder.encode(fileName, "UTF-8");
		response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xls");
		response.setContentType("application/vnd.ms-excel");
		OutputStream os = response.getOutputStream();
		workbook.write(os);
		workbook.close();
		os.flush();
		os.close();
	}

}
